package com.lw.service.impl;

import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.crypto.digest.BCrypt;
import com.lw.common.enums.GlobalTypeEnum;
import com.lw.common.utils.GlobalConstants;
import com.lw.common.utils.StrUtils;
import com.lw.common.vo.Option;
import com.lw.common.vo.PasswordVo;
import com.lw.common.vo.UserVo;
import com.lw.entity.TbUserRole;
import com.lw.service.TbRoleMenuService;
import com.lw.service.TbUserRoleService;
import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.core.query.QueryChain;
import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import com.lw.entity.TbUser;
import com.lw.mapper.TbUserMapper;
import com.lw.service.TbUserService;
import jakarta.annotation.Resource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;

import java.beans.Transient;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

import static com.lw.entity.table.TbDeptTableDef.TB_DEPT;
import static com.lw.entity.table.TbUserTableDef.TB_USER;

/**
 * 系统用户 服务层实现。
 *
 * @author lw
 * @since 2024年12月08日 07:53:39
 */
@Service
public class TbUserServiceImpl extends ServiceImpl<TbUserMapper, TbUser> implements TbUserService {
    private static final Logger logger = LoggerFactory.getLogger(TbUserServiceImpl.class);

    @Resource
    private TbUserRoleService tbUserRoleService;

    @Resource
    private TbRoleMenuService tbRoleMenuService;

    @Override
    public TbUser getByUserName(String username) {
        QueryWrapper queryWrapper = new QueryWrapper();
        queryWrapper.eq("username", username);
        return getOne(queryWrapper);
    }

    @Override
    public TbUser getUserInfo(Long id) {
        QueryWrapper queryWrapper = new QueryWrapper();
        queryWrapper.select(TB_USER.ALL_COLUMNS);
        queryWrapper.eq(TB_USER.ID.getName(), id);
        TbUser user = getOne(queryWrapper);
        //查询用户角色集合
        Set<Long> role = tbUserRoleService.getRoleListByUserId(id);
        //查询用户权限集合
        if (CollectionUtil.isNotEmpty(role)) {
            user.setRoleIds(role);
            Set<String> perms = tbRoleMenuService.getRolePerms(role);
            user.setPerms(perms);
        }
        return user;
    }

    @Override
    public Page<TbUser> getUserPage(UserVo userVo) {
        long pageNumber = userVo.getPageNum();
        long pageSize = userVo.getPageSize();
        Page<TbUser> page = new Page<>(pageNumber, pageSize);

        QueryChain<TbUser> queryWrapper = queryChain()
                .select(TB_USER.ALL_COLUMNS, TB_DEPT.NAME.as("deptName"))
                .from(TB_USER).leftJoin(TB_DEPT)
                .on(TB_USER.DEPT_ID.eq(TB_DEPT.ID));
        try {
            StpUtil.checkRole(GlobalConstants.ROOT_ROLE_NAME);
        } catch (Exception e) {
            //排除角色code是ROOT的用户
            queryWrapper.and(TB_USER.ID.notIn(1));
        }
        if (StrUtils.hasLength(userVo.getKeywords())) {
            queryWrapper.where(TB_USER.USERNAME.like(userVo.getKeywords()).or(TB_USER.NICKNAME.like(userVo.getKeywords())));
        }

        //状态
        if (userVo.getStatus() != null) {
            queryWrapper.and(TB_USER.STATUS.eq(userVo.getStatus()));
        }
        //部门
        if (StrUtils.hasLength(userVo.getDeptId())) {
            queryWrapper.and(TB_USER.DEPT_ID.eq(userVo.getDeptId()));
        }
        //时间
        if (CollectionUtil.isNotEmpty(userVo.getCreateTime())) {
            queryWrapper.and(TB_USER.CREATE_TIME.between(userVo.getCreateTime().get(0), userVo.getCreateTime().get(1)));
        }
        queryWrapper.orderBy(TB_USER.CREATE_TIME.desc());
        return page(page, queryWrapper);
    }

    @Override
    public Boolean saveUser(TbUser tbUser) {
        String username = tbUser.getUsername();
        if (StrUtils.isEmpty(username)) {
            throw new RuntimeException("用户名不能为空");
        }
        QueryWrapper queryWrapper = new QueryWrapper();
        queryWrapper.eq(TB_USER.USERNAME.getName(), username);
        long count = count(queryWrapper);
        Assert.isTrue(count == 0, "用户名已存在");
        //设置默认密码
        String hashPw = BCrypt.hashpw(GlobalConstants.DEFAULT_PASSWORD, BCrypt.gensalt(GlobalConstants.LOG_ROUNDS));
        tbUser.setPassword(hashPw);
        save(tbUser);
        //保存用户角色
        Set<Long> roleIds = tbUser.getRoleIds();
        if (CollectionUtil.isNotEmpty(roleIds)) {
            List<TbUserRole> userRolesList = new ArrayList<>(roleIds.size());
            for (Long roleId : roleIds) {
                TbUserRole tbUserRole = new TbUserRole();
                tbUserRole.setRoleId(roleId);
                tbUserRole.setUserId(tbUser.getId());
                userRolesList.add(tbUserRole);
            }
            tbUserRoleService.saveBatch(userRolesList);
        }
        return null;
    }

    @Transactional(rollbackFor = Exception.class, isolation = Isolation.DEFAULT) // 事务隔离界别 默认重复读
    @Override
    @CacheEvict(value = "permissionCache", key = "#tbUser.id") // 清除缓存
    public Boolean updateUser(TbUser tbUser) {
        tbUser.setPassword(null);
        QueryWrapper queryWrapper = new QueryWrapper();
        queryWrapper.ne(TB_USER.ID.getName(), tbUser.getId());
        queryWrapper.eq(TB_USER.USERNAME.getName(), tbUser.getUsername());
        long count = count(queryWrapper);
        Assert.isTrue(count == 0, "用户名已存在");
        boolean update = updateById(tbUser);
        logger.info("更新用户信息：{}", update);
        Set<Long> roleIds = tbUser.getRoleIds();
        if (CollectionUtil.isNotEmpty(roleIds)) {
            //先删除用户与角色关系
            boolean b = tbUserRoleService.deleteUserRoleByUserId(tbUser.getId());
            logger.info("删除用户与角色关系：{}", b);

            List<TbUserRole> userRolesList = new ArrayList<>(roleIds.size());
            for (Long roleId : roleIds) {
                TbUserRole tbUserRole = new TbUserRole();
                tbUserRole.setRoleId(roleId);
                tbUserRole.setUserId(tbUser.getId());
                userRolesList.add(tbUserRole);
            }
            boolean b1 = tbUserRoleService.saveBatch(userRolesList);
            logger.info("保存用户与角色关系：{}", b1);
        }

        return null;
    }

    @Transactional
    @Override
    public Boolean deleteUser(String ids) {
        String[] split = ids.split(",");
        for (String id : split) {
            TbUser user = getById(id);
            if (user == null) {
                throw new RuntimeException("用户不存在");
            }
            logger.info("1.删除用户：{}", user.getUsername());
            //删除用户与角色关系
            boolean b1 = tbUserRoleService.deleteUserRoleByUserId(Long.valueOf(id));
            logger.info("2.删除用户与角色关系：{}", b1);
            //删除用户
            boolean b = removeById(id);
            logger.info("3.删除用户：{}", b);
        }
        return true;
    }

    @Override
    public Boolean resetPwd(Long id, String password) {
        QueryWrapper queryWrapper = new QueryWrapper();
        queryWrapper.eq(TB_USER.ID.getName(), id);
        TbUser tbUser = getOne(queryWrapper);
        if (tbUser == null) {
            throw new RuntimeException("用户不存在");
        }
        //重置密码
        String hashPw = BCrypt.hashpw(StrUtils.hasLength(password) ? password : GlobalConstants.DEFAULT_PASSWORD, BCrypt.gensalt(GlobalConstants.LOG_ROUNDS));
        tbUser.setPassword(hashPw);
        boolean update = updateById(tbUser);
        logger.info("重置密码：{}", update);
        return update;
    }

    @Override
    public List<TbUser> getUserList(UserVo userVo) {
        QueryChain<TbUser> queryWrapper = queryChain()
                .select(TB_USER.ALL_COLUMNS, TB_DEPT.NAME.as("deptName"))
                .from(TB_USER).leftJoin(TB_DEPT)
                .on(TB_USER.DEPT_ID.eq(TB_DEPT.ID));
        try {
            StpUtil.checkRole(GlobalConstants.ROOT_ROLE_NAME);
        } catch (Exception e) {
            //排除角色code是ROOT的用户
            queryWrapper.and(TB_USER.ID.notIn(1));
        }
        if (StrUtils.hasLength(userVo.getKeywords())) {
            queryWrapper.where(TB_USER.USERNAME.like(userVo.getKeywords()).or(TB_USER.NICKNAME.like(userVo.getKeywords())));
        }

        //状态
        if (userVo.getStatus() != null) {
            queryWrapper.and(TB_USER.STATUS.eq(userVo.getStatus()));
        }
        //部门
        if (StrUtils.hasLength(userVo.getDeptId())) {
            queryWrapper.and(TB_USER.DEPT_ID.eq(userVo.getDeptId()));
        }
        //时间
        if (CollectionUtil.isNotEmpty(userVo.getCreateTime())) {
            queryWrapper.and(TB_USER.CREATE_TIME.between(userVo.getCreateTime().get(0), userVo.getCreateTime().get(1)));
        }
        return list(queryWrapper);
    }

    //修改密码
    @Override
    public Boolean updatePassword(PasswordVo passwordVo) {
        Long id = StpUtil.getLoginIdAsLong();
        TbUser tbUser = getById(id);
        if (tbUser == null) {
            throw new RuntimeException("用户不存在");
        }
        String oldPassword = passwordVo.getOldPassword();
        String newPassword = passwordVo.getNewPassword();
        if (StrUtils.isEmpty(oldPassword) || StrUtils.isEmpty(newPassword)) {
            throw new RuntimeException("密码不能为空");
        }
        if (!BCrypt.checkpw(oldPassword, tbUser.getPassword())) {
            throw new RuntimeException("原密码错误");
        }
        String hashPw = BCrypt.hashpw(newPassword, BCrypt.gensalt(GlobalConstants.LOG_ROUNDS));
        tbUser.setPassword(hashPw);
        return updateById(tbUser);
    }

    @Override
    public List<Option<Long>> getUserOptionList() {
        QueryWrapper queryWrapper = new QueryWrapper();
        queryWrapper.eq(TB_USER.STATUS.getName(), GlobalTypeEnum.STATE_ENABLE.getValue());
        List<TbUser> list = list(queryWrapper);
        return list.stream().map(tbUser -> {
            Option<Long> option = new Option<>();
            option.setLabel(tbUser.getNickname());
            option.setValue(tbUser.getId());
            return option;
        }).toList();
    }

    @Override
    public Boolean updateUserProfile(TbUser tbUser) {
        long loginId = StpUtil.getLoginIdAsLong();
        tbUser.setId(loginId);
        tbUser.setPassword(null);
        tbUser.setUpdateBy(loginId);
        return updateById(tbUser);
    }

}
